Skip to content

feat(earn): add geo-blocking for mUSD conversion feature#24501

Merged
nickewansmith merged 17 commits into
mainfrom
MUSD-194-as-meta-mask-legal-i-want-to-be-able-to-geofence-the-convert-feature-so-that-it-is-not-available-in-countries-where-it-is-not-compliant
Jan 21, 2026
Merged

feat(earn): add geo-blocking for mUSD conversion feature#24501
nickewansmith merged 17 commits into
mainfrom
MUSD-194-as-meta-mask-legal-i-want-to-be-able-to-geofence-the-convert-feature-so-that-it-is-not-available-in-countries-where-it-is-not-compliant

Conversation

@nickewansmith
Copy link
Copy Markdown
Contributor

@nickewansmith nickewansmith commented Jan 13, 2026

Description

What is the reason for the change?

As MetaMask Legal, we need to geofence the mUSD conversion feature so it is not available in countries where it is not compliant. This ensures regulatory compliance by restricting access to the convert feature in specific regions.

What is the improvement/solution?

This PR implements geo-blocking for the mUSD conversion feature using a two-tier approach:

  • LaunchDarkly (Primary): Remote feature flag earnMusdConversionGeoBlockedCountries with { blockedCountries: ["GB", ...] } structure
  • Environment Variable (Fallback): MM_MUSD_CONVERSION_GEO_BLOCKED_COUNTRIES as comma-separated country codes (e.g., "GB,US")
    The implementation leverages the existing RampsController geolocation data and integrates geo-blocking checks everywhere the mUSD conversion flow is enabled.
    Key changes:
  • New selectMusdConversionBlockedCountries selector with LD + env var fallback
  • New useMusdConversionEligibility hook combining geolocation with blocked countries
  • Geo-blocking integrated into: useMusdCtaVisibility, EarnBalance, EarnLendingBalance, TokenListItem, and useCustomAmount

Changelog

CHANGELOG entry: Added geo-blocking for mUSD conversion feature to restrict access in non-compliant countries

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/MUSD-194

Manual testing steps

Feature: mUSD Conversion Geo-blocking

  Scenario: UK user cannot see mUSD conversion CTA
    Given user is located in the UK (GB)
    And user has a convertible stablecoin (USDC, DAI, USDT)
    And MM_MUSD_CONVERSION_GEO_BLOCKED_COUNTRIES="GB" or LD flag blocks GB

    When user views their token list
    Then user does not see "Convert to mUSD" CTA
    And user cannot access the mUSD conversion flow

  Scenario: US user can see mUSD conversion CTA
    Given user is located in the US
    And user has a convertible stablecoin (USDC, DAI, USDT)
    And GB is the only blocked country

    When user views their token list
    Then user sees "Convert to mUSD" CTA
    And user can initiate the mUSD conversion flow

  Scenario: Fallback to env var when LaunchDarkly unavailable
    Given LaunchDarkly flag earnMusdConversionGeoBlockedCountries is not configured
    And MM_MUSD_CONVERSION_GEO_BLOCKED_COUNTRIES="GB" is set

    When selector evaluates blocked countries
    Then GB is returned as a blocked country

Screenshots/Recordings

Before

n/a

After

n/a

Pre-merge author checklist

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

Implements geofencing for the mUSD conversion experience across Earn surfaces, driven by remote config with a local fallback.

  • Adds selectMusdConversionBlockedCountries (LD earnMusdConversionGeoBlockedCountries{ blockedRegions: string[] }) with env fallback MM_MUSD_CONVERSION_GEO_BLOCKED_COUNTRIES and parseBlockedCountriesEnv; defaults to ['GB'] via DEFAULT_MUSD_BLOCKED_COUNTRIES
  • Introduces useMusdConversionEligibility (uses getDetectedGeolocation; blocks by default when undefined) returning isEligible, isLoading, geolocation, blockedCountries
  • Gates UI/flows by geo-eligibility: useMusdCtaVisibility (Buy/Get, token-list-item, asset-overview CTAs), EarnBalance (requires isGeoEligible for isConvertibleStablecoin), EarnLendingBalance (asset overview CTA), TokenListItem and Tokens (list CTA), useCustomAmount (mUSD tag)
  • Updates .js.env.example with MM_MUSD_CONVERSION_GEO_BLOCKED_COUNTRIES and adds constants in musd.ts
  • Extensive unit tests added/updated to cover geo-eligible/blocked paths across hooks and components

Written by Cursor Bugbot for commit 96cb7be. This will update automatically on new commits. Configure here.

@github-actions
Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@nickewansmith nickewansmith marked this pull request as ready for review January 13, 2026 21:59
@nickewansmith nickewansmith requested review from a team as code owners January 13, 2026 21:59
Comment thread app/components/UI/Earn/selectors/featureFlags/index.ts
Comment thread app/components/UI/Earn/selectors/featureFlags/index.ts
@nickewansmith nickewansmith force-pushed the MUSD-194-as-meta-mask-legal-i-want-to-be-able-to-geofence-the-convert-feature-so-that-it-is-not-available-in-countries-where-it-is-not-compliant branch 2 times, most recently from c7b8d6b to 5daf61a Compare January 14, 2026 16:30
bergarces
bergarces previously approved these changes Jan 15, 2026
Copy link
Copy Markdown
Contributor

@bergarces bergarces left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok from Assets

Let us know when the conflicts are resolved and we'll re-approve.

@nickewansmith nickewansmith force-pushed the MUSD-194-as-meta-mask-legal-i-want-to-be-able-to-geofence-the-convert-feature-so-that-it-is-not-available-in-countries-where-it-is-not-compliant branch from 5daf61a to e9d1bbd Compare January 15, 2026 13:01
@github-actions github-actions Bot added size-XL and removed size-L labels Jan 15, 2026
Matt561
Matt561 previously approved these changes Jan 15, 2026
Copy link
Copy Markdown
Contributor

@Matt561 Matt561 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-approving so you're not blocked. We should double-check if we want to block or allow by default.

Comment thread app/components/UI/Earn/hooks/useMusdConversionEligibility.ts Outdated
@nickewansmith nickewansmith force-pushed the MUSD-194-as-meta-mask-legal-i-want-to-be-able-to-geofence-the-convert-feature-so-that-it-is-not-available-in-countries-where-it-is-not-compliant branch from 426125b to 99b4a75 Compare January 15, 2026 18:07
@nickewansmith nickewansmith requested a review from Matt561 January 15, 2026 18:08
Comment thread app/components/UI/Earn/hooks/useMusdConversionEligibility.ts
Comment thread app/components/UI/Earn/selectors/featureFlags/index.ts
…e-to-geofence-the-convert-feature-so-that-it-is-not-available-in-countries-where-it-is-not-compliant
Comment thread app/components/UI/Earn/components/EarnBalance/EarnBalance.test.tsx
Matt561
Matt561 previously approved these changes Jan 20, 2026
cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@nickewansmith nickewansmith requested a review from Matt561 January 20, 2026 20:48
Matt561
Matt561 previously approved these changes Jan 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: None (no tests recommended)
  • Risk Level: low
  • AI Confidence: 85%
click to see 🤖 AI reasoning details

This PR adds geo-blocking functionality for the mUSD conversion feature within the Earn module. The changes are:

  1. Highly scoped: All changes are contained within the Earn/mUSD feature components and hooks
  2. Feature-flagged: The mUSD conversion feature is controlled by remote feature flags (selectIsMusdConversionFlowEnabledFlag)
  3. No E2E test coverage exists: Grep search confirmed no E2E tests exist for "musd", "mUSD", "MUSD", or "Earn" in the e2e directory
  4. Well unit-tested: The PR includes comprehensive unit tests for all new functionality (useMusdConversionEligibility, selectMusdConversionBlockedCountries, parseBlockedCountriesEnv)
  5. Additive changes: The geo-blocking adds an additional check (isGeoEligible) to existing conditions - it doesn't modify core logic
  6. No impact on core flows: The changes don't affect wallet creation, account management, network switching, confirmations, or any other core functionality

The changes affect:

  • EarnBalance component (adds geo-eligibility check)
  • Tokens component (adds geo-eligibility check for mUSD CTA)
  • useMusdCtaVisibility hook (adds geo-blocking checks)
  • useCustomAmount hook (adds geo-eligibility check for mUSD conversion transactions)

Since there are no E2E tests for the Earn/mUSD feature, and the changes are purely additive geo-blocking logic that doesn't affect any existing E2E test flows, no E2E tags need to be run.

View GitHub Actions results

Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

showNetworkIcon: false,
selectedChainId: null,
};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing isEmptyWallet in geo-blocked return path

Medium Severity

The shouldShowBuyGetMusdCta function's return object when the user is geo-blocked is missing the isEmptyWallet property that all other return paths include. The function's documented return type and consumer code (MusdConversionAssetListCta) expect isEmptyWallet to always be present. While the current component early-returns when shouldShowCta is false (preventing immediate runtime issues), this inconsistency breaks the function's contract, causes type inference issues, and could lead to bugs if consuming code is refactored.

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't break anything, I'll address it in another PR.

@sonarqubecloud
Copy link
Copy Markdown

@nickewansmith nickewansmith requested a review from Matt561 January 20, 2026 23:39
@nickewansmith nickewansmith added this pull request to the merge queue Jan 21, 2026
Merged via the queue into main with commit a7809e8 Jan 21, 2026
59 of 60 checks passed
@nickewansmith nickewansmith deleted the MUSD-194-as-meta-mask-legal-i-want-to-be-able-to-geofence-the-convert-feature-so-that-it-is-not-available-in-countries-where-it-is-not-compliant branch January 21, 2026 02:35
@github-actions github-actions Bot locked and limited conversation to collaborators Jan 21, 2026
@metamaskbot metamaskbot added the release-7.63.0 Issue or pull request that will be included in release 7.63.0 label Jan 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-7.63.0 Issue or pull request that will be included in release 7.63.0 size-XL team-earn

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants